﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Utils.Physics2D.Objects;
using Utils.MathUtilitys;

namespace Utils.Physics2D.Collision
{
    public class AxisRectCollider : CollisionHandler
    {
        float width, height;

        public AxisRectCollider(PhysicsObject owner, float width, float height) : base(owner)
        {
            this.width = width;
            this.height = height;
        }

        public override bool CheckCollision(PhysicsObject obj, CollisionHandler.DISPLACE displacement)
        {
            /*
             * collision detection based on 
             * http://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection 
             * answer 17
             */
            Vector2 dir = obj.Pos - owner.Pos;
            Vector2 distance = new Vector2(MathUtils.Abs(obj.Pos.X - owner.Pos.X), MathUtils.Abs(obj.Pos.Y - owner.Pos.Y));
            if (distance.X > (width / 2 + obj.Radius) || distance.Y > (width / 2 + obj.Radius))
            {
                return false;
            }

            /*
             * check if circle is inside rect or collides with one of the edges
             */
            if (distance.X <= width / 2 || distance.Y <= height /2)
            {
                if (displacement != DISPLACE.NONE)
                {
                    bool pushVertical = false;
                    bool pushHorizontal = false;

                    if (distance.X > height / 2)
                    {
                        // circle collides outside with the left or right edge
                        // push it into the direction of the edge
                        pushHorizontal = true;
                    }
                    else if (distance.Y > width / 2)
                    {
                        // circle collides outside with the top or bottom edge
                        // push it into the direction of the edge
                        pushVertical = true;
                    }
                    else
                    {
                        // circle is inside rect
                        // push it by impact
                        Vector2 objDir = obj.Pos - obj.OldPos;
                        Vector2 selfDir = owner.Pos - owner.OldPos;
                        Vector2 impactDir = objDir - selfDir;
                        if (MathUtils.Abs(impactDir.X) > MathUtils.Abs(impactDir.Y))
                        {
                            pushHorizontal = true;
                        }
                        else
                        {
                            pushVertical = true;
                        }
                    }

                    if (pushHorizontal)
                    {
                        PushByDir(obj, displacement, new Vector2((width / 2 + obj.Radius - distance.X) * MathUtils.Sign(dir.X), 0));
                    }
                    else if (pushVertical)
                    {
                        PushByDir(obj, displacement, new Vector2(0, (height / 2 + obj.Radius - distance.Y) * MathUtils.Sign(dir.Y)));
                    }
                }

                return true;
            }

            /*
             * check if circle collides with one of the corners and push it out
             */
            float cornerDistanceSqr = MathUtils.Sqr(distance.X - width / 2) + MathUtils.Sqr(distance.Y - height / 2);

            if (cornerDistanceSqr <= MathUtils.Sqr(obj.Radius))
            {
                if (displacement != DISPLACE.NONE)
                {
                    Vector2 corner = owner.Pos;
                    corner.X += width / 2 * MathUtils.Sign(dir.X);
                    corner.Y += height / 2 * MathUtils.Sign(dir.Y);
                    Vector2 pushDir = obj.Pos - corner;
                    float distanceToCorner = pushDir.Length();
                    pushDir *= (obj.Radius - distanceToCorner) / distanceToCorner;

                    PushByDir(obj, displacement, pushDir);
                }

                return true;
            }

            return false;
        }
    }
}
